<?php

/**
 * 房源队列消耗
 */

namespace app\crontab\controller\once;

use app\common\lib\RedisKey;
use app\common\lib\RespCode;
use app\crontab\model\House;
use app\crontab\model\HouseAli;
use app\crontab\model\HouseAuction;
use app\crontab\model\HouseJd;
use app\crontab\model\Region;
use custom\Log;
use custom\Redis;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
use think\Exception;

class Queue extends Command {

    /**
     * redis数据库id
     */
    const DB_ID = 2;

    protected $houseModel;
    protected $houseJdModel;
    protected $houseAliModel;
    protected $houseAuctionModel;
    protected $regionModel;
    protected $isWork = false;

    public function __construct($name = null) {
        parent::__construct($name);
        $this->houseModel = new House;
        $this->houseJdModel = new HouseJd;
        $this->houseAliModel = new HouseAli;
        $this->houseAuctionModel = new HouseAuction;
        $this->regionModel = new Region;
    }

    protected function configure() {
        $this->setName('queue');
    }

    protected function execute(Input $input, Output $output) {
        // 关闭日志写入
        \think\facade\Log::close();
        $redisModel = Redis::getInstance();
        $redisModel->select(self::DB_ID);
        while (true) {
            $rData = $params = [];

            try {
                if ($rData = $redisModel->brpop(RedisKey::HOUSE_QUEUE, 3)) {
                    echo date("Y-m-d H:i:s") . "\n" . $rData[1] . "\n";
                    $params = json_decode($rData[1], true);
                    // 验证参数
                    $this->checkParams($params, ['cmd', 'data']);
                    // 房源数据处理
                    if ($params['cmd'] == 'house-info') {
                        $params['data'] = $this->handleData($params['data']);
                    }

                    Db::startTrans();
                    $this->isWork = true;
                    // 模型选择
                    $this->changeModel($params);
                    Db::commit();


                    // 新增房源,消息通知集合
                    if (isset($params['add_house'])) {
                        \push\House::statistics($params, "house");
                    }

                    Db::close();
                }
            } catch (Exception $e) {
                $this->isWork && Db::rollback();
                Db::close();
                // 不是参数错误时，重写队列
                if ($e->getCode() != RespCode::PARAMS_ERR && isset($rData[1])) {
                    Log::write($rData[1], "queue_err_data.log");
                    // 处理失败重新写入队列
                    $redisModel->rpush(RedisKey::HOUSE_QUEUE, $rData[1]);
                }
                $err = $e->getMessage() . ' (' . $e->getCode() . '), ' . $e->getFile() . ' (' . $e->getLine() . ')' . "\n" . $e->getTraceAsString() . "\r\n\r\n";
                Log::write($err, "queue.log");
            }

            // 重置数据
            $this->resetData();
            if (memory_get_usage() > 30 * 1024 * 1024) {
                echo "内存溢出\n";
                exit;//大于30M内存退出程序，防止内存泄漏被系统杀死导致任务终端
            }
        }
    }

    /**
     * 重置数据
     * @param $rData
     * @param $params
     */
    private function resetData() {
        $this->isWork = false;
    }

    /**
     * 验证参数
     * @param $params
     * @param $fields
     * @throws Exception
     */
    private function checkParams(&$params, $fields) {
        foreach ($fields as $key => $field) {
            if (!isset($params[$field]) || (empty($params[$field]) && $params[$field] !== 0)) {
                throw new Exception("{$field} 参数错误", RespCode::PARAMS_ERR);
            }
        }
    }

    /**
     * 数据处理
     * @param $params
     * @return array
     * @throws Exception
     */
    private function handleData($params) {
        $isUpdate = (isset($params['is_update']) && $params['is_update'] != 1) || !isset($params['is_update']);
        // 通过标题title获取地理信息
        $address = !empty($params['address']) ? "{$params['province']}{$params['city']}{$params['area']}{$params['address']}" : $params['title'];
        $data = $this->houseModel->getAddressInfoByMap($address, $params['lat'], $params['lng']);
        // 城市code集合
        if (isset($data['city_code'])) {
            if (!$pCodes = db('region')->where(['city_code' => $data['city_code']])->value('p_codes')) {
                $data['city_code'] = 0;
            }
            $data['city_codes'] = "{$pCodes}{$data['city_code']},";
        }
        $params['address'] = $params['address'] ?? ($data['map_address'] ?? '');

        // 添加时，才上传图片
        if ($isUpdate) {
            $imgs = $this->houseModel->batchUpFileByOss($params['cover'], 'house');
            $data['cover'] = $imgs[0] ?? '';
            $data['pic'] = implode(',', $imgs);
        } else {
            unset($params['cover'], $params['pic']);
        }
        $params = array_merge($params, $data);


        // 数据格式转换
        $params['source_id'] = $params['id'];
        !empty($params['start_time']) && $params['start_time'] = strtotime($params['start_time']);
        !empty($params['end_time']) && $params['end_time'] = strtotime($params['end_time']);
        $params['build_area'] = floatInt($params['build_area'], 4);
        $params['initial_price'] = floatInt($params['initial_price'], 2);
        $params['type'] == \app\api\model\HouseAuction::TYPE_4 && $params['pay_selloff'] = floatInt($params['initial_price'], 2);
        $params['price_lower_offset'] = floatInt($params['price_lower_offset'], 2);
        $params['bond'] = floatInt($params['bond'], 2);
        $params['consult_price'] = floatInt($params['consult_price'], 2);
        $params['final_price'] = floatInt($params['final_price'], 2);
        !empty($params['house_type']) && $params['house_type_id'] = $params['house_type'];
        $params['sn'] = md5($params['title']);
        $params['month_itime'] = strtotime(date("Y-m"));
        $params['itime'] = time();
        $params['utime'] = $params['itime'];
        $params['day_itime'] = strtotime(date("Y-m-d"));
        // 折扣比,保留两位小数
        $params['discount'] = -1;
        // 评估价存在，且评估价!=初始价格时
        if (!empty($params['consult_price']) && $params['consult_price'] != $params['initial_price']) {
            $params['discount'] = round($params['initial_price'] / $params['consult_price'] * 10, 2);
            $params['discount'] < 1 && $params['discount'] = 0;
            $params['discount'] >= 10 && $params['discount'] = 10;
        }
        unset($params['id']);
        return $params;
    }

    /**
     * 模块选择
     * @param $params
     * @return string
     * @throws Exception
     */
    private function changeModel(&$params) {
        switch ($params['cmd']) {
            case "house-info":
                $this->houseInfo($params['data']);
                break;
            case "house-village":
                $this->houseVillage($params['data']);
                break;
            default:
                throw new Exception("cmd 类型错误", RespCode::PARAMS_ERR);
                break;
        }
    }

    /**
     * 房源拍卖信息
     * @param $params
     * @throws Exception
     */
    private function houseInfo(&$params) {
        // 三方房源
        if ($params['source_type'] == 1) {
            $this->houseJdModel->edit($params);
        } else {
            $this->houseAliModel->edit($params);
        }

        // 区域房源信息统计
        if (!empty($params['city_code']) && isset($params['add_house'])) {
            $this->regionModel->incOne($params['city_code']);
        }

        // 房源拍卖信息
        $this->houseAuctionModel->edit($params);

        // 房源基本信息
        $this->houseModel->edit($params);

        // 房源拍卖信息绑定
        $this->houseAuctionModel->bindHouseId($params);

    }

    /**
     * 房源小区信息
     * @param $params
     * @throws Exception
     * @throws \think\exception\PDOException
     */
    public function houseVillage($params) {
        $this->houseModel->bindVillage($params);
    }

}